import pandas as pd
import re
import json

# 业务域元数据（一级/二级业务域中英文对照）
BUSINESS_DOMAINS = {
    "一级业务域": {
        "中文": ['电网资源业务中台', '项目管理业务中台', '客户服务业务中台', '财务管理业务中台'],
        "英文缩写": {'电网资源业务中台': 'pgp', '项目管理业务中台': 'pmp', 
                   '客户服务业务中台': 'emss', '财务管理业务中台': 'fmp'}
    },
    "二级业务域": {
        "中文校验规则": re.compile(r'^[\u4e00-\u9fff]+$'),  # 中文名称必须全汉字
        "英文校验规则": {
            "长度": 4,  # 英文缩写不超过4位
            "格式": re.compile(r'^[a-z]+$')  # 必须全小写字母
        }
    }
}

file_path = '财务中台测试(1).xlsx'

def read_excel_file(file_path):
    inspect_results = []
    total_errors = 0  # 初始化错误总数计数器
    sheet_name = '填表说明'
    excel_file = pd.ExcelFile(file_path)
    # 获取所有 Sheet 名称
    sheet_names = excel_file.sheet_names
    for sheet in sheet_names:
        if sheet != sheet_name:
            df = excel_file.parse(sheet_name=sheet)
            columns_data = []
            for col in df.columns:
                col_data = df[col].tolist()
                columns_data.append(col_data)
            # 检查sheet页数据标准情况
            inspect_result, error_count = inspect_all(columns_data, sheet)
            inspect_results.append(inspect_result)
            total_errors += error_count  # 累加错误数量
    
    result = " ".join(inspect_results)
    # 添加总错误数到结果
    result += f"\n\n共发现 {total_errors} 个问题"
    
    # 打印结果并写入txt文件
    print(result)
    with open('inspection_report.txt', 'w', encoding='utf-8') as f:
        f.write(result)
    return result

def inspect_all(columns_data, sheet):
    errors = []
    error_count = 0  # 本sheet的错误计数器
    
    if sheet.endswith("微服务"):
        error_list = validate_fifth_column_service_naming(columns_data)
        if error_list:
            errors.append(format_errors(error_list))
            error_count += count_errors(error_list)
        
        error_list = validate_sixth_column_service_naming(columns_data)
        if error_list:
            errors.append(format_errors(error_list))
            error_count += count_errors(error_list)
            
    elif sheet.endswith("服务"):
        error_list = validate_business_platforms_ch_st(columns_data)
        if error_list:
            errors.append(format_errors(error_list))
            error_count += count_errors(error_list)
        
        error_list = validate_business_platforms_ch_nd(columns_data)
        if error_list:
            errors.append(format_errors(error_list))
            error_count += count_errors(error_list)
        
        error_list = validate_third_column_service_naming(columns_data)  # 中文
        if error_list:
            errors.append(format_errors(error_list))
            error_count += count_errors(error_list)
        
        error_list = validate_fourth_column_service_naming(columns_data)  # 英文
        if error_list:
            errors.append(format_errors(error_list))
            error_count += count_errors(error_list)
            
    elif sheet.endswith("接口"):
        error_list = validate_eleventh_column_method(columns_data)
        if error_list:
            errors.append(format_errors(error_list))
            error_count += count_errors(error_list)
            
    elif sheet.startswith("出入参信息"):
        error_list = validate_thirteenth_column_data(columns_data)
        if error_list:
            errors.append(format_errors(error_list))
            error_count += count_errors(error_list)
        
        error_list = validate_fourteenth_column_data(columns_data)
        if error_list:
            errors.append(format_errors(error_list))
            error_count += count_errors(error_list)

    result = " ".join(errors)
    return result, error_count

def format_errors(error_list):
    """将错误列表转换为字符串，保持原有格式但统一行号输出"""
    formatted_errors = []
    for error in error_list:
        # 保持原有错误信息格式，只修改行号部分
        if isinstance(error, tuple) and len(error) > 0:
            # 处理行号：确保是数字格式
            line_num = error[0]
            if isinstance(line_num, int):
                # 保持原有格式，只修改行号部分
                new_error = (str(line_num),) + error[1:]
                formatted_errors.append("|".join(str(item) for item in new_error))
            else:
                formatted_errors.append("|".join(str(item) for item in error))
        else:
            formatted_errors.append(str(error))
    return " ".join(formatted_errors)

def count_errors(error_list):
    """统计错误数量（每个错误条目计数一次）"""
    return len(error_list)

# ========== 以下校验函数保持原样 ==========
def validate_business_platforms_ch_st(columns_data):
    # 修正键名："中文名称" → "中文"
    valid_business_platforms = BUSINESS_DOMAINS["一级业务域"]["中文"]  # 原错误键名已修改
    valid_entries = []
    invalid_entries = []
    tmp_entries = []
    for i, cell_value in enumerate(columns_data[1]):
        if cell_value in valid_business_platforms:
            valid_entries.append((i + 1, cell_value))
        else:
            tmp_entries.append((i+1, cell_value))
    # 优化：仅保留唯一的错误条目（避免重复日志）
    seen = set()
    unique_tmp = []
    for item in tmp_entries:
        key = (item[0], item[1])  # 以行号+值作为去重键
        if key not in seen:
            seen.add(key)
            unique_tmp.append(item)
    tmp_entries = unique_tmp

    if tmp_entries:
        for entry in tmp_entries:
            invalid_entries.append((entry[0], entry[1], '不符合5.2.1业务域命名规则中【强制】一级业务域中文命名，应为各业务中台中文名称，分别为电网资源业务中台、项目管理业务中台、客户服务业务中台、财务管理业务中台。'))
    return invalid_entries  # 只返回错误记录

def validate_business_platforms_en_st(columns_data):
    platform_to_abbreviation = BUSINESS_DOMAINS["一级业务域"]["英文缩写"]
    valid_entries = []  # 保存有效条目供后续关联检查
    non_matching_entries = []
    tmp_entries = []
    for i, (platform, abbreviation) in enumerate(zip(columns_data[0], columns_data[1])):
        expected_abbreviation = platform_to_abbreviation.get(platform)
        if expected_abbreviation and abbreviation == expected_abbreviation and abbreviation.islower():
            valid_entries.append((i + 1, platform, abbreviation))  # 记录有效行号和值
        else:
            tmp_entries.append((i + 1, platform, abbreviation))
    # 合并错误数据
    if tmp_entries:
        for entry in tmp_entries:
            non_matching_entries.append((entry[0], entry[1], entry[2], '不符合5.2.1业务域命名规则中【强制】一级业务域英文缩写命名，应由小写字母构成，电网资源业务中台应为 pgp，项目管理业务中台应为 pmp，客户服务业务中台应为 emss，财务管理业务中台应为 fmp。'))
    return non_matching_entries  # 只返回错误记录

def validate_business_platforms_ch_nd(columns_data):
    non_chinese_entries = []
    tmp_entries = []
    chinese_pattern = re.compile(r'^[\u4e00-\u9fff]+$')
    for i, third_col in enumerate(columns_data[2]):
        if not chinese_pattern.match(str(third_col)):
            tmp_entries.append((i, third_col))

    #合并错误数据
    if tmp_entries:
        for entry in tmp_entries:
            non_chinese_entries.append((entry[0], entry[1], '不符合5.2.1业务域命名规则中【强制】二级业务域中文命名，应为各中台共享服务中心中文名称或业务模块中文名称。'))
    return non_chinese_entries

def validate_business_platforms_en_nd(columns_data):
    # 原硬编码的长度和格式改为从配置获取
    rule = BUSINESS_DOMAINS["二级业务域"]["英文校验规则"]
    non_matching_entries = []
    tmp_entries = []
    for i, fourth_col in enumerate(columns_data[3]):
        fourth_col_str = str(fourth_col)
        if not re.fullmatch(rule["格式"], fourth_col_str):  # 使用正则校验格式
            tmp_entries.append((i + 1, fourth_col_str))
        elif len(fourth_col_str) > rule["长度"]:  # 使用配置的长度限制
            tmp_entries.append((i + 1, fourth_col_str))

    if tmp_entries:
        for entry in tmp_entries:
            error_description = '不符合5.2.1业务域命名规则中【强制】二级业务域英文缩写命名，应由小写字母构成且不超过四位。英文缩写规则由各业务中台内部统一制定。'
            non_matching_entries.append((entry[0], entry[1], error_description))

    return non_matching_entries

def is_chinese(text):
    for char in text:
        if not ('\u4e00' <= char <= '\u9fff'):
            return False
    return True

def validate_third_column_service_naming(columns_data):
    invalid_entries = []
    tmp_entries1 = []
    
    for i, third_col in enumerate(columns_data[3]):
        third_col_str = str(third_col)
        parts = third_col_str.split('-')
        # 分段检查优先于内容检查
        if len(parts) != 3:
            tmp_entries1.append((i, third_col_str, "格式错误：未采用三段式（一级业务域-二级业务域-服务中文名）"))
        else:
            # 仅当是三段式时才检查字符和一级业务域
            if not all(is_chinese(part) for part in parts):
                tmp_entries1.append((i, third_col_str, "包含非中文字符"))
    
    if tmp_entries1:
        for entry in tmp_entries1:
            invalid_entries.append((entry[0], entry[1], entry[2], '不符合5.2.2功能服务命名中【强制】功能服务中文命名，应采用三段式，体现其一级业务域、二级业务域，多级元素之间以短横线分隔。格式应为：一级业务域-二级业务域-服务中文名'))
    return invalid_entries

def validate_fourth_column_service_naming(columns_data):
    invalid_entries = []
    # 固定要求的一级业务域英文缩写集合（pgp/pmp/emss/fmp）
    valid_first_segments = {'pgp', 'pmp', 'emss', 'fmp'}
    
    for i, fourth_col in enumerate(columns_data[4]):
        fourth_col_str = str(fourth_col)
        parts = fourth_col_str.split('-')
        
        error_msg = None
        # 1. 分段数校验（最多5段）
        if len(parts) > 5:
            error_msg = "分段过多：最多5段"
        # 2. 一级缩写校验（必须为pgp/pmp/emss/fmp）
        elif len(parts) < 1:
            error_msg = "格式错误：至少应有一段"
        elif parts[0] not in valid_first_segments:
            error_msg = f"一级英文缩写错误：应为pgp/pmp/emss/fmp中的一个，实际为{parts[0]}"
        # 3. 二级缩写格式校验（小写字母且长度≤4）
        elif len(parts) >= 2:
            second_part = parts[1]
            if len(second_part) > BUSINESS_DOMAINS["二级业务域"]["英文校验规则"]["长度"]:
                error_msg = "二级英文缩写过长：不超过4位"
            elif not BUSINESS_DOMAINS["二级业务域"]["英文校验规则"]["格式"].match(second_part):
                error_msg = "二级英文缩写格式错误：需全小写字母"
        
        if error_msg:
            invalid_entries.append((i + 1, fourth_col_str, error_msg))
    
    # 合并错误信息（明确问题类型）
    if invalid_entries:
        result = []
        for entry in invalid_entries:
            result.append((entry[0], entry[1], entry[2], '不符合5.2.2功能服务命名中【强制】功能服务英文命名规则，具体问题见错误详情'))
        return result
    return []

def validate_fifth_column_service_naming(columns_data):
    invalid_entries = []
    tmp_entries1 = []
    
    for i, fifth_col in enumerate(columns_data[1]):
        fifth_col_str = str(fifth_col)
        parts = fifth_col_str.split('-')
        # 仅保留两段式格式验证和全中文字符验证
        if len(parts) != 2:
            tmp_entries1.append((i + 1, fifth_col_str, "格式错误：应采用两段式（业务域-微服务功能中文名）"))
        elif not all(is_chinese(part) for part in parts):
            tmp_entries1.append((i + 1, fifth_col_str, "包含非中文字符"))

    if tmp_entries1:
        for entry in tmp_entries1:
            invalid_entries.append((entry[0], entry[1], entry[2], '不符合5.2.3微服务命名中【强制】微服务中文命名，应采用两段式，名称中元素之间以短横线分隔。格式应为：业务域-微服务功能中文名'))
    return invalid_entries

def validate_sixth_column_service_naming(columns_data):
    invalid_entries = []
    tmp_entries1 = []
    # 从 BUSINESS_DOMAINS 获取所有有效一级业务域英文缩写（pgp/pmp/emss/fmp）
    valid_abbrevs = set(BUSINESS_DOMAINS["一级业务域"]["英文缩写"].values())
    error_messages = {
        'too_many_parts': "不符合5.2.3微服务命名中【强制】微服务英文命名，应采用分段方式，应体现所属的一级业务域及微服务语义，多级元素之间以短横线分隔，最多为五段。其中第一段内容为固定格式（一级业务域英文缩写），后四段由各业务中台按需定义。格式应为：一级业务域英文缩写-xxx-xxx-xxx-xxx。",
        'wrong_first_part': "不符合5.2.3微服务命名中【强制】微服务英文命名，应采用分段方式，应体现所属的一级业务域及微服务语义，多级元素之间以短横线分隔，最多为五段。其中第一段内容为固定格式（一级业务域英文缩写），后四段由各业务中台按需定义。格式应为：一级业务域英文缩写-xxx-xxx-xxx-xxx。"
    }

    for i, sixth_col in enumerate(columns_data[2]):
        sixth_col_str = str(sixth_col)
        parts = sixth_col_str.split('-')
        # 分段数验证（最多5段）
        if len(parts) > 5:
            tmp_entries1.append((i + 1, sixth_col_str, "分段过多：最多5段"))
        # 一级缩写验证（必须属于 pgp/pmp/emss/fmp）
        elif parts[0] not in valid_abbrevs:
            tmp_entries1.append((i + 1, sixth_col_str, f"一级业务域英文缩写错误：应为{list(valid_abbrevs)}中的一个"))
        # 可选：新增二级缩写格式验证（根据需求）
        elif len(parts)>=2 and (len(parts[1]) > BUSINESS_DOMAINS["二级业务域"]["英文校验规则"]["长度"] or 
                              not BUSINESS_DOMAINS["二级业务域"]["英文校验规则"]["格式"].match(parts[1])):
            tmp_entries1.append((i + 1, sixth_col_str, "二级业务域英文缩写错误：需为全小写字母且长度≤4"))

    if tmp_entries1:
        for entry in tmp_entries1:
            invalid_entries.append((entry[0], entry[1], entry[2], error_messages['too_many_parts']))
    return invalid_entries

def validate_eighth_column_service_naming(columns_data):
    invalid_entries = []
    tmp_entries1 = []
    for i, (package_name, english_name) in enumerate(zip(columns_data[8], columns_data[7])):
        if not package_name.startswith(english_name):
            tmp_entries1.append((i + 1, english_name, package_name))

    if tmp_entries1:
        for entry in tmp_entries1:
            invalid_entries.append((entry[0], entry[1], entry[2], '不符合5.2.3微服务命名中【强制】微服务英文命名和微服务具体部署介质，应能够直接对应。'))
    return invalid_entries

def is_camel_case(s):
    return bool(re.match(r'^[A-Z][a-zA-Z0-9]*(?:[A-Z][a-zA-Z0-9]*)*$', s))

def is_lower_camel_case(s):
    return bool(re.match(r'^[a-z][a-zA-Z0-9]*(?:[A-Z][a-zA-Z0-9]*)*$', s))

def is_simplified_verb_noun_structure(text):
    words = re.findall(r'[a-z]+|[A-Z][a-zA-Z0-9]*', text)

    if len(words) >= 2 and all(char.islower() for char in words[0]) and words[1][0].isupper():
        return True
    return False

def validate_tenth_column_path_format(columns_data):
    invalid_entries = []

    for i, tenth_col in enumerate(columns_data[11]):
        tenth_col_str = str(tenth_col)
        parts = tenth_col_str.split('/')
        row_errors = []
        error_messages = {
            'err1':'不符合5.3.1接口路径命名中【推荐】接口各段路径命名宜采用驼峰命名法，使用动词+宾语结构，词汇来自业务中台基础词库，各中台可在基础词库的基础上维护自己的标准词库。不建议使用“-”“_”“.”分隔英文单词。基础词库参考附录A《业务中台基础词库》。',
            'err2':'不符合5.3.1接口路径命名中【强制】接口路径格式应为：[/安全域标识/资源名称/执行动作]，安全域标识各中台按需省略，资源名称支持层级结构。针对客户服务业务中台面向社会开放接口，为保障信息系统安全，可不体现资源及执行动作信息。'
        }
        if re.search(r'[0-9-_.]', tenth_col_str):
            row_errors.append(error_messages['err1'])
        if len(parts) == 1:
            row_errors.append(error_messages['err2'])
        else:
            for j, part in enumerate(parts):
                if not (is_lower_camel_case(part) or is_camel_case(part)):
                    if error_messages['err1'] not in row_errors:
                        row_errors.append(error_messages['err1'])
                if j == len(parts) - 1 and not is_simplified_verb_noun_structure(part):
                    if error_messages['err2'] not in row_errors:
                        row_errors.append(error_messages['err2'])
        if row_errors:
            invalid_entries.append((i + 1, tenth_col_str, "; ".join(row_errors)))

    return invalid_entries

def validate_eleventh_column_method(columns_data):
    valid_methods = {'GET', 'POST', 'get', 'post'}
    invalid_entries = []
    tmp_entries1 = []
    for i, eleventh_col in enumerate(columns_data[7]):
        eleventh_col_str = str(eleventh_col).strip()
        if eleventh_col_str not in valid_methods:
            tmp_entries1.append((i + 1, eleventh_col_str))

    if tmp_entries1:
        for entry in tmp_entries1:
            invalid_entries.append((entry[0], entry[1], '不符合5.2.2接口访问类型中【强制】接口访问类型，应为 GET或 POST方式，不能是其他访问类型。'))
    return invalid_entries

def validate_thirteenth_column_data(columns_data):
    invalid_entries = []
    valid_key_regex = re.compile(r'^[a-zA-Z0-9_]{1,64}$')  # 5.3.3.1 长度和字符限制
    pinyin_regex = re.compile(r'^[a-z]{4,}$')  # 优化拼音判断（至少4字母降低误判）
    
    for row_idx, thirteenth_col in enumerate(columns_data[3], start=1):  # 行号从1开始
        cell_value = str(thirteenth_col).strip()
        if not cell_value:
            continue  # 跳过空单元格
        
        try:
            data = json.loads(cell_value)
            if not isinstance(data, dict):
                err_msg = f"参数结构错误，需为JSON对象"
                invalid_entries.append((row_idx, cell_value, err_msg))
                continue

            # 按行隔离参数缓存（同单元格内参数唯一）
            param_cache = set()
            for key in data.keys():
                # 5.3.3.1 参数命名规则校验
                if not valid_key_regex.match(key):
                    err_msg = f"参数名'{key}'不符合要求（仅允许字母/数字/下划线，长度1-64）"
                    invalid_entries.append((row_idx, key, err_msg))
                
                # 拼音命名校验
                if pinyin_regex.match(key):
                    err_msg = f"参数名'{key}'禁止使用汉语拼音"
                    invalid_entries.append((row_idx, key, err_msg))
                
                # 同单元格内参数唯一性校验
                if key in param_cache:
                    err_msg = f"参数名'{key}'在本单元格内重复"
                    invalid_entries.append((row_idx, key, err_msg))
                param_cache.add(key)

        except json.JSONDecodeError:
            err_msg = f"单元格内容非有效JSON格式（内容：{cell_value}）"
            invalid_entries.append((row_idx, cell_value, err_msg))
    
    return invalid_entries

def validate_json_structure(json_data, business_prefixes):
    errors = []
    if not isinstance(json_data, dict):
        errors.append("违反5.3.4.2【强制】接口出参未采用Json格式")
        return errors
    
    # 5.3.4.3 返回码格式检查
    code = json_data.get('code', '')
    if isinstance(code, str):
        # 通用返回码验证（6位数字）
        if re.match(r'^\d{6}$', code):
            # 6位数字的返回码均符合通用返回码规范（000000为正确，其他为状态码）
            pass
        else:
            # 业务返回码验证（一级业务域英文缩写前缀+最多6位数字）
            valid_prefix = any(code.startswith(prefix) for prefix in business_prefixes)
            suffix = code[len(max(business_prefixes, key=len)):] if valid_prefix else ""
            if not valid_prefix or not re.match(r'^\d{1,6}$', suffix):
                errors.append(f"违反5.3.4.3【强制】业务返回码{code}格式错误（需一级业务域前缀+最多6位数字）")
    else:
        errors.append("违反5.3.4.3【强制】返回码code类型应为String")
    
    # 5.3.4.2 message检查（非空时不含服务端错误信息）
    message = json_data.get('message', '')
    if isinstance(message, str) and 'error' in message.lower():
        errors.append("违反5.3.4.2【强制】message包含服务端具体代码错误信息")
    elif not isinstance(message, str):
        errors.append("违反5.3.4.2【强制】message类型应为String")
    
    # 5.3.4.2 data类型检查
    if not isinstance(json_data.get('data', None), dict):
        errors.append("违反5.3.4.2【强制】data类型应为Object")
    
    return errors

def validate_fourteenth_column_data(columns_data):
    invalid_entries = []
    business_prefixes = list(BUSINESS_DOMAINS["一级业务域"]["英文缩写"].values())  # 一级业务域英文缩写
    
    for i, cell_value in enumerate(columns_data[5]):  
        # 处理Excel空单元格（NaN会被pandas读取为float类型）
        if pd.isna(cell_value):
            json_str = "{}"  # 空单元格默认视为空JSON对象
        else:
            json_str = str(cell_value)  # 非空值转换为字符串
        
        try:
            json_data = json.loads(json_str)
        except json.JSONDecodeError:
            invalid_entries.append((i+1, json_str, "违反5.3.4.2【强制】接口出参未采用Json格式"))
            continue
        
        # 检查参数命名（递归遍历所有键名）
        param_errors = []
        def check_param_names(obj, parent_path=""):
            if isinstance(obj, dict):
                for key, value in obj.items():
                    current_path = f"{parent_path}.{key}" if parent_path else key
                    # 5.3.4.1 参数命名规则检查
                    if not re.match(r'^[a-zA-Z0-9_]{1,64}$', key) or re.search(r'[a-zA-Z]+\d+[a-zA-Z]', key):  # 禁止拼音（简单校验：字母+数字+字母组合可能为拼音）
                        param_errors.append(f"参数{current_path}违反5.3.4.1【强制】命名规则（仅允许字母/数字/下划线，≤64字符，禁止拼音）")
                    check_param_names(value, current_path)
            elif isinstance(obj, list):
                for item in obj:
                    check_param_names(item, parent_path)
        
        check_param_names(json_data)
        if param_errors:
            invalid_entries.append((i+1, json_str, "; ".join(param_errors)))
        
        # 检查JSON结构和返回码
        structure_errors = validate_json_structure(json_data, business_prefixes)
        if structure_errors:
            invalid_entries.append((i+1, json_str, "; ".join(structure_errors)))
    
    return invalid_entries

# 主程序入口
if __name__ == "__main__":
    # 执行Excel文件检查
    inspection_result = read_excel_file(file_path)
    
    # 输出到控制台
    print("数据标准化检查结果：")
    print(inspection_result)